/**
 * 
 */
package qy.jalgotrade.stratanalyzer;

import java.time.Duration;
import java.time.ZonedDateTime;

import qy.jalgotrade.bar.Bars;
import qy.jalgotrade.strategy.BaseStrategy;

/**
 * A :class:`pyalgotrade.stratanalyzer.StrategyAnalyzer` that calculates max. drawdown and longest
 * drawdown duration for the portfolio.
 * 
 * @author c-geo
 *
 */
public class DrawDown extends StrategyAnalyzer {

	protected class DrawDownHelper {

		private double __highWatermark;

		private double __lowWatermark;

		private double __lastLow;

		private ZonedDateTime __highDateTime;

		private ZonedDateTime __lastDateTime;

		public DrawDownHelper() {

			__highWatermark = Double.NaN;
			__lowWatermark = Double.NaN;
			__lastLow = Double.NaN;
			__highDateTime = null;
			__lastDateTime = null;
		}

		/**
		 * The drawdown duration, not necessarily the max drawdown duration.
		 * 
		 * @return
		 */
		protected Duration getDuration() {

			return Duration.between(__highDateTime, __lastDateTime);
		}

		protected double getMaxDrawDown() {

			return (__lowWatermark - __highWatermark) / __highWatermark;
		}

		protected double getCurrentDrawDown() {

			return (__lastLow - __highWatermark) / __highWatermark;
		}

		protected void update(ZonedDateTime dateTime, double low, double high) {

			assert low <= high;
			__lastLow = low;
			__lastDateTime = dateTime;

			if (Double.isNaN(__highWatermark) || high >= __highWatermark) {
				__highWatermark = high;
				__lowWatermark = low;
				__highDateTime = dateTime;
			} else {
				__lowWatermark = Math.min(__lowWatermark, low);
			}
		}
	}

	private double __maxDD;

	private Duration __longestDDDuration;

	private DrawDownHelper __currDrawDown;

	/**
	 * 
	 */
	public DrawDown() {

		super();
		__maxDD = 0.0;
		__longestDDDuration = Duration.ZERO;
		__currDrawDown = new DrawDownHelper();
	}

	/**
	 * Returns the max. (deepest) drawdown.
	 * 
	 * @return
	 */
	public double getMaxDrawDown() {

		return Math.abs(__maxDD);
	}

	/**
	 * Returns the duration of the longest drawdown.
	 * 
	 * .. note:: Note that this is the duration of the longest drawdown, not necessarily the deepest
	 * one.
	 * 
	 * @return
	 */
	public Duration getLongestDrawDownDuration() {

		return __longestDDDuration;
	}

	public double calculateEquity(BaseStrategy strat) {

		return strat.getBroker().getEquity();
	}

	/*
	 * (non-Javadoc)
	 * @see qy.jalgotrade.stratanalyzer.StrategyAnalyzer#beforeOnBars(qy.jalgotrade.strategy.BaseStrategy, qy.jalgotrade.bar.Bars)
	 */
	@Override
	public void beforeOnBars(BaseStrategy strat, Bars bars) {

		double equity = calculateEquity(strat);
		__currDrawDown.update(bars.getDateTime(), equity, equity);
		if (__currDrawDown.getDuration().compareTo(__longestDDDuration) > 0) {
			__longestDDDuration = __currDrawDown.getDuration();
		}
		if (__currDrawDown.getMaxDrawDown() < __maxDD) {
			__maxDD = __currDrawDown.getMaxDrawDown();
		}
	}
}
